/*
Copyright SecureKey Technologies Inc. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/
package chpvdr

import (
	reqContext "context"

	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/common/logging"
	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/common/options"
	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/ichain/chconfig"
	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/ichain/transactor"
	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/providers/context"
	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/providers/ichain"
	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/util/concurrent/lazycache"
)

var logger = logging.NewLogger("ichainsdk")

type ChainProvider struct {
	providerContext context.Providers
	ctxtCaches      *lazycache.Cache
}

func New(config ichain.EndpointConfig, opts ...options.Opt) (*ChainProvider, error) {
	return &ChainProvider{
		ctxtCaches: lazycache.New(
			"Client_Context_Cache",
			func(key lazycache.Key) (interface{}, error) {
				ck := key.(*ctxtCacheKey)
				return newContextCache(ck.context, opts), nil
			},
		),
	}, nil
}

func (cp *ChainProvider) Initialize(providers context.Providers) error {
	cp.providerContext = providers
	return nil
}

func (cp *ChainProvider) Close() {
	cp.ctxtCaches.Close()
}

func (cp *ChainProvider) CloseContext(ctx ichain.ClientContext) {
	key, err := newCtxtCacheKey(ctx)
	if err != nil {
		logger.Warnf("Unable to close context: %s", err)
		return
	}
	logger.Warnf("Deleting context cache...")
	cp.ctxtCaches.Delete(key)
}

func (cp *ChainProvider) ChainService(ctx ichain.ClientContext, chainID string) (ichain.ChainService, error) {
	key, err := newCtxtCacheKey(ctx)
	if err != nil {
		return nil, err
	}

	ctxtCache, err := cp.ctxtCaches.Get(key)
	if err != nil {
		// This should never happen since the creation of a cache never returns an error
		return nil, err
	}

	return &ChainService{
		provider:  cp,
		context:   ctx,
		chainID:   chainID,
		ctxtCache: ctxtCache.(*contextCache),
	}, nil
}

type ChainService struct {
	provider  *ChainProvider
	context   context.Client
	chainID   string
	ctxtCache *contextCache
}

func (cs *ChainService) ConfigService() (ichain.ChainConfigService, error) {
	return chconfig.NewChainConfigService(cs.chainID)
}

// EventService 缓存event service
// @param opts
// @return ichain.EventService
// @return error
func (cs *ChainService) EventService(opts ...options.Opt) (ichain.EventService, error) {
	return cs.ctxtCache.GetEventService(cs.chainID, opts...)
}

// Membership 缓存链的成员
// @return ichain.ChainMembership
// @return error
func (cs *ChainService) Membership() (ichain.ChainMembership, error) {
	return cs.ctxtCache.GetMembership(cs.chainID)
}

// ChainConfig 缓存链配置，避免大量重复请求inode节点
// @return ichain.ChainCfg
// @return error
func (cs *ChainService) ChainConfig() (ichain.ChainCfg, error) {
	return cs.ctxtCache.GetChainConfig(cs.chainID)
}

func (cs *ChainService) Transactor(reqCtx reqContext.Context) (ichain.Transactor, error) {
	return transactor.NewTransactor(reqCtx, cs.chainID), nil
}

func (cs *ChainService) Discovery() (ichain.DiscoveryService, error) {
	return cs.ctxtCache.GetDiscoveryService(cs.chainID)
}
